/*
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is MPEG4IP.
 *
 * The Initial Developer of the Original Code is Cisco Systems Inc.
 * Portions created by Cisco Systems Inc. are
 * Copyright (C) Cisco Systems Inc. 2001 - 2004.  All Rights Reserved.
 *
 * 3GPP features implementation is based on 3GPP's TS26.234-v5.60,
 * and was contributed by Ximpo Group Ltd.
 *
 * Portions created by Ximpo Group Ltd. are
 * Copyright (C) Ximpo Group Ltd. 2003, 2004.  All Rights Reserved.
 *
 * Contributor(s):
 *      Dave Mackie     dmackie@cisco.com
 *              Ximpo Group Ltd.        mp4v2@ximpo.com
 */

#ifndef MP4V2_IMPL_MP4TRACK_H
#define MP4V2_IMPL_MP4TRACK_H

namespace mp4v2
{
    namespace impl
    {

        ///////////////////////////////////////////////////////////////////////////////

        typedef uint32_t MP4ChunkId;

        // forward declarations
        class MP4File;
        class MP4Atom;
        class MP4Property;
        class MP4IntegerProperty;
        class MP4Integer8Property;
        class MP4Integer16Property;
        class MP4Integer32Property;
        class MP4Integer64Property;
        class MP4StringProperty;

        class MP4Track
        {
        public:
            MP4Track(MP4File &file, MP4Atom &trakAtom);

            virtual ~MP4Track();

            MP4TrackId GetId()
            {
                return m_trackId;
            }

            const char *GetType();

            void SetType(const char *type);

            MP4File &GetFile()
            {
                return m_File;
            }

            MP4Atom &GetTrakAtom()
            {
                return m_trakAtom;
            }

            void ReadSample(
                // input parameters
                MP4SampleId sampleId,
                // output parameters
                uint8_t     **ppBytes,
                uint32_t     *pNumBytes,
                MP4Timestamp *pStartTime = NULL,
                MP4Duration  *pDuration = NULL,
                MP4Duration  *pRenderingOffset = NULL,
                bool         *pIsSyncSample = NULL,
                bool         *hasDependencyFlags = NULL,
                uint32_t     *dependencyFlags = NULL );

            void WriteSample(
                const uint8_t *pBytes,
                uint32_t numBytes,
                MP4Duration duration = 0,
                MP4Duration renderingOffset = 0,
                bool isSyncSample = true);

            void WriteSampleDependency(
                const uint8_t *pBytes,
                uint32_t       numBytes,
                MP4Duration    duration,
                MP4Duration    renderingOffset,
                bool           isSyncSample,
                uint32_t       dependencyFlags );

            virtual void FinishWrite(uint32_t options = 0);

            uint64_t    GetDuration();      // in track timeScale units
            uint32_t    GetTimeScale();
            uint32_t    GetNumberOfSamples();
            uint32_t    GetSampleSize(MP4SampleId sampleId);
            uint32_t    GetMaxSampleSize();
            uint64_t    GetTotalOfSampleSizes();
            uint32_t    GetAvgBitrate();    // in bps
            uint32_t    GetMaxBitrate();    // in bps

            MP4Duration GetFixedSampleDuration();
            void        SetFixedSampleDuration(MP4Duration duration);

            void        GetSampleTimes(MP4SampleId sampleId,
                                       MP4Timestamp *pStartTime, MP4Duration *pDuration);

            bool        IsSyncSample(MP4SampleId sampleId);

            MP4SampleId GetSampleIdFromTime(
                MP4Timestamp when,
                bool wantSyncSample = false);

            MP4Duration GetSampleRenderingOffset(MP4SampleId sampleId);
            void        SetSampleRenderingOffset(MP4SampleId sampleId,
                                                 MP4Duration renderingOffset);

            MP4EditId   AddEdit(
                MP4EditId editId = MP4_INVALID_EDIT_ID);

            void        DeleteEdit(
                MP4EditId editId);

            MP4Timestamp GetEditStart(
                MP4EditId editId);

            MP4Timestamp GetEditTotalDuration(
                MP4EditId editId);

            MP4SampleId GetSampleIdFromEditTime(
                MP4Timestamp editWhen,
                MP4Timestamp *pStartTime = NULL,
                MP4Duration *pDuration = NULL);

            // special operation for use during hint track packet assembly
            void ReadSampleFragment(
                MP4SampleId sampleId,
                uint32_t sampleOffset,
                uint16_t sampleLength,
                uint8_t *pDest);

            // special operations for use during optimization

            uint32_t GetNumberOfChunks();

            MP4Timestamp GetChunkTime(MP4ChunkId chunkId);

            void ReadChunk(MP4ChunkId chunkId,
                           uint8_t **ppChunk, uint32_t *pChunkSize);

            void RewriteChunk(MP4ChunkId chunkId,
                              uint8_t *pChunk, uint32_t chunkSize);

            MP4Duration GetDurationPerChunk();
            void        SetDurationPerChunk( MP4Duration );

        protected:
            bool        InitEditListProperties();

            File       *GetSampleFile( MP4SampleId sampleId );
            uint64_t    GetSampleFileOffset(MP4SampleId sampleId);
            uint32_t    GetSampleStscIndex(MP4SampleId sampleId);
            uint32_t    GetChunkStscIndex(MP4ChunkId chunkId);
            uint32_t    GetChunkSize(MP4ChunkId chunkId);
            uint32_t    GetSampleCttsIndex(MP4SampleId sampleId,
                                           MP4SampleId *pFirstSampleId = NULL);
            MP4SampleId GetNextSyncSample(MP4SampleId sampleId);

            void UpdateSampleSizes(MP4SampleId sampleId,
                                   uint32_t numBytes);
            bool IsChunkFull(MP4SampleId sampleId);
            void UpdateSampleToChunk(MP4SampleId sampleId,
                                     MP4ChunkId chunkId, uint32_t samplesPerChunk);
            void UpdateChunkOffsets(uint64_t chunkOffset);
            void UpdateSampleTimes(MP4Duration duration);
            void UpdateRenderingOffsets(MP4SampleId sampleId,
                                        MP4Duration renderingOffset);
            void UpdateSyncSamples(MP4SampleId sampleId,
                                   bool isSyncSample);

            MP4Atom *AddAtom(const char *parentName, const char *childName);

            void UpdateDurations(MP4Duration duration);
            MP4Duration ToMovieDuration(MP4Duration trackDuration);

            void UpdateModificationTimes();

            void WriteChunkBuffer();

            void CalculateBytesPerSample();

            void FinishSdtp();

        protected:
            MP4File    &m_File;
            MP4Atom    &m_trakAtom;         // moov.trak[]
            MP4TrackId  m_trackId;          // moov.trak[].tkhd.trackId
            MP4StringProperty *m_pTypeProperty; // moov.trak[].mdia.hdlr.handlerType

            uint32_t m_lastStsdIndex;
            File    *m_lastSampleFile;

            // for efficient construction of hint track packets
            MP4SampleId m_cachedReadSampleId;
            uint8_t    *m_pCachedReadSample;
            uint32_t    m_cachedReadSampleSize;

            // for writing
            MP4SampleId m_writeSampleId;
            MP4Duration m_fixedSampleDuration;
            uint8_t    *m_pChunkBuffer;
            uint32_t    m_chunkBufferSize;          // Actual size of our chunk buffer.
            uint32_t    m_sizeOfDataInChunkBuffer;  // Size of the data in the chunk buffer.
            uint32_t    m_chunkSamples;
            MP4Duration m_chunkDuration;

            // controls for chunking
            uint32_t    m_samplesPerChunk;
            MP4Duration m_durationPerChunk;

            uint32_t       m_bytesPerSample;

            // controls for AMR chunking
            int     m_isAmr;
            uint8_t m_curMode;

            MP4Integer32Property *m_pTimeScaleProperty;
            MP4IntegerProperty *m_pTrackDurationProperty;       // 32 or 64 bits
            MP4IntegerProperty *m_pMediaDurationProperty;       // 32 or 64 bits
            MP4IntegerProperty *m_pTrackModificationProperty;   // 32 or 64 bits
            MP4IntegerProperty *m_pMediaModificationProperty;   // 32 or 64 bits

            MP4Integer32Property *m_pStszFixedSampleSizeProperty;
            MP4Integer32Property *m_pStszSampleCountProperty;

            void SampleSizePropertyAddValue(uint32_t bytes);
            uint8_t m_stsz_sample_bits;
            bool m_have_stz2_4bit_sample;
            uint8_t m_stz2_4bit_sample_value;
            MP4IntegerProperty *m_pStszSampleSizeProperty;

            MP4Integer32Property *m_pStscCountProperty;
            MP4Integer32Property *m_pStscFirstChunkProperty;
            MP4Integer32Property *m_pStscSamplesPerChunkProperty;
            MP4Integer32Property *m_pStscSampleDescrIndexProperty;
            MP4Integer32Property *m_pStscFirstSampleProperty;

            MP4Integer32Property *m_pChunkCountProperty;
            MP4IntegerProperty   *m_pChunkOffsetProperty;       // 32 or 64 bits

            MP4Integer32Property *m_pSttsCountProperty;
            MP4Integer32Property *m_pSttsSampleCountProperty;
            MP4Integer32Property *m_pSttsSampleDeltaProperty;

            // for improve sequental timestamp index access
            uint32_t    m_cachedSttsIndex;
            MP4SampleId m_cachedSttsSid;
            MP4Timestamp    m_cachedSttsElapsed;

            uint32_t    m_cachedCttsIndex;
            MP4SampleId m_cachedCttsSid;

            MP4Integer32Property *m_pCttsCountProperty;
            MP4Integer32Property *m_pCttsSampleCountProperty;
            MP4Integer32Property *m_pCttsSampleOffsetProperty;

            MP4Integer32Property *m_pStssCountProperty;
            MP4Integer32Property *m_pStssSampleProperty;

            MP4Integer32Property *m_pElstCountProperty;
            MP4IntegerProperty   *m_pElstMediaTimeProperty;     // 32 or 64 bits
            MP4IntegerProperty   *m_pElstDurationProperty;      // 32 or 64 bits
            MP4Integer16Property *m_pElstRateProperty;
            MP4Integer16Property *m_pElstReservedProperty;

            string m_sdtpLog; // records frame types for H264 samples

            //////////
        public:
            uint64_t GetChunkSize( void );
            void ResetChunkSize( void );

        private:
            uint64_t m_ui64ChunkSize;
        };

        MP4ARRAY_DECL(MP4Track, MP4Track *);

        ///////////////////////////////////////////////////////////////////////////////

    }
} // namespace mp4v2::impl

#endif // MP4V2_IMPL_MP4TRACK_H
